package com.tansun.ruledata.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.drools.mvel.MVELConstraint;
import org.kie.api.KieBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tansun.rule.config.AppConfiguration;
import com.tansun.rule.pojo.EnterPoint;
import com.tansun.rule.utils.AppCacheInfo;
import com.tansun.rule.utils.DroolsRuleLog;
import com.tansun.rule.utils.IOUtils;
import com.tansun.rule.utils.MClass;
import com.tansun.rule.utils.MField;
import com.tansun.rule.utils.RuleBomMapJsonConvert;
import com.tansun.rule.utils.RuleBomMapXmlConvertProject;
import com.tansun.rule.utils.StringUtils;
import com.tansun.ruledata.common.RuleConstants;
import com.tansun.ruledata.data.entity.DeployRuleLogs;
import com.tansun.ruledata.data.service.BussinessSynchService;
import com.tansun.ruledata.data.service.RuleDataEngineService;
import com.tansun.ruledata.data.service.RuleResourceLoadService;
import com.tansun.ruledata.service.RuleDataBusinessService;
import com.tansun.ruledata.service.RuleDataCompileService;
import com.tansun.ruledata.service.RuleFlowParamService;
import com.tansun.ruledata.service.RuleLogService;
import com.tansun.ruledata.service.export.RuleFlowParamExportService;

@Service("ruleDataBusinessService")
public class RuleDataBusinessServiceImpl implements RuleDataBusinessService {
	 /**
     * 日志
     */
	private Logger loggger = LoggerFactory.getLogger(this.getClass());
	/**
	 * JavaUnmarshaller
	 */
	ExecutorService logExecutorService = Executors.newFixedThreadPool(6);
	/**
	 * 规则引擎服务
	 */
	@Autowired
	private RuleDataEngineService ruleEngineService;
	@Autowired
	private  RuleResourceLoadService ruleResourceLoadService;
	@Autowired
	private  RuleFlowParamExportService  ruleFlowParamExportService;
	@Autowired
	private RuleFlowParamService ruleFlowParamService;
	@Autowired
	private RuleDataCompileService ruleDataCompileService;
	@Autowired
	private RuleLogService ruleLogService;
	@Autowired
	private BussinessSynchService bussinessSynchService;

	/**
	 * 规则服务入口
	 * ruleProjectName	:项目名称
	 * enterPoint		:入口点
	 * businessInputXml	:数据xml
	 */
	public String ruleEngineService(String ruleProjectName,String enterPoint,String inputType,String businessInput) {
		String resultString = "";
		try {
			Map<String, MClass> metaMap =AppConfiguration.RuleProjMetaMap.get(ruleProjectName);
			if (metaMap == null  || metaMap.isEmpty()){
				ruleResourceLoadService.loadProjectDataMapping(ruleProjectName);
				metaMap=AppConfiguration.RuleProjMetaMap.get(ruleProjectName);
			}
			//根据input string 转换对象
			Map<String, Object> input = RuleBomMapJsonConvert.json2bom(businessInput, metaMap);//by wangyanghui 20190121,与规则计算保存一致，可将数据进行类型自动转换
			//默认走冠军
			JSONObject jsonObj= new JSONObject();
			jsonObj.put("champion", "true");
			input.put("championInfo", jsonObj);
			resultString = ruleEngineService(ruleProjectName,enterPoint,inputType,input,null);
		}catch (Exception e) {
			e.printStackTrace();
			// TODO: handle exception
		}
		return resultString;
	}
	
	public Map<String, Object> ruleEngineService(String ruleProjectName,String enterPoint,Map<String, Object> busiInput){
		return ruleEngineService( ruleProjectName, enterPoint, busiInput, StringUtils.getUUID());
	}
	public Map<String, Object> ruleEngineService(String projectCode,String enterPoint,Map<String, Object> busiInput,String uuidStr){
		//异常结果标识1:报文格式异常,2:规则执行异常，3:规则部署等待，4:其他异常
		//输出对象
		long start = System.currentTimeMillis();
		Map<String, Object> output = new HashMap<String, Object>();
		Map<String, Object> sys = new HashMap<String, Object>();
		DroolsRuleLog droolsRuleLog = new DroolsRuleLog(true);
		sys.put("invokeId", uuidStr);
		sys.put("enterPoint", enterPoint);
		sys.put("projectCode", projectCode);
		loggger.info("invokeId:"+sys.get("invokeId")+",projectCode："+projectCode+",enterpointer:"+enterPoint+",businessInput:"+JSON.toJSONString(busiInput));
		try {
			//调用服务
			//默认走冠军
			JSONObject jsonObj= new JSONObject();
			jsonObj.put("champion", "true");
			busiInput.put("championInfo", jsonObj);
			output = ruleEngineService.invokeRule(projectCode, enterPoint,"json", busiInput, droolsRuleLog);
			sys.put("isSuccess", "1");
		} catch (Exception e) {
//			e.printStackTrace();
			sys.put("exception", IOUtils.getStackTraceMsg(e));
			sys.put("isSuccess", "0");
			sys.put("exceptionType", "4");
		}finally {
			sys.put("invokeTimes", System.currentTimeMillis() - start);
			sys.put("ruleExecutResultList", droolsRuleLog.getLogs());
			output.put("System", sys);
		}
		log(projectCode,enterPoint,busiInput,output,sys,droolsRuleLog,null,uuidStr);
		return output;
	}
	public String ruleEngineService(String projectCode,String enterPoint,String inputType,Map<String, Object> busiInput,String batchNo){
		//异常结果标识1:报文格式异常,2:规则执行异常，3:规则部署等待，4:其他异常
		//输出对象
		Map<String, Object> output = new HashMap<String, Object>();
		Map<String, Object> sys = new HashMap<String, Object>();
		DroolsRuleLog droolsRuleLog = new DroolsRuleLog(true);
		sys.put("invokeId", StringUtils.getUUID());
		sys.put("enterPoint", enterPoint);
		loggger.info("invokeId:"+sys.get("invokeId")+",projectCode："+projectCode+",enterpointer:"+enterPoint+",businessInput:"+JSON.toJSONString(busiInput));
		long start = System.currentTimeMillis();
		try {
			//计算衍生变量
			RuleBomMapXmlConvertProject.doDerives(busiInput, projectCode);
			//调用服务
			output = ruleEngineService.invokeRule(projectCode, enterPoint,inputType, busiInput, droolsRuleLog);
			sys.put("ruleExecutResultList", droolsRuleLog.getLogs());
			output = droolsRuleLog.getScoreTrailMap(output);
//		//其他异常
		} catch (Exception e) {
			sys.put("exception", IOUtils.getStackTraceMsg(e));
			sys.put("exceptionType", "4");
		}finally {
			if(sys.get("exceptionType")!=null  && sys.get("exceptionType").toString().length()>0) {
				sys.put("isSuccess", "0");
			}else {
				sys.put("isSuccess", "1");
			}
			sys.put("invokeTimes", System.currentTimeMillis() - start);
			output.put("System", sys);
		}
		log(projectCode,enterPoint,busiInput,output,sys,droolsRuleLog,batchNo,null);
		return JSON.toJSONString(output);
	}
	@Override
	public Map<String, Object> ruleEngineService_ruleExe(String projectCode ,String enterPoint ,Map<String, Object> busiInput){
		return ruleEngineService_ruleExe(projectCode,enterPoint,busiInput,StringUtils.getUUID());
	}
	public Map<String, Object> ruleEngineService_ruleExe(String projectCode ,String enterPoint ,Map<String, Object> busiInput,String batchNo){
		//输出对象
		Map<String, Object> output = new HashMap<String, Object>();
		Map<String, Object> sys = new HashMap<String, Object>();
		DroolsRuleLog droolsRuleLog = new DroolsRuleLog(true);
		sys.put("invokeId", StringUtils.getUUID());
		sys.put("enterPoint", enterPoint);
		loggger.info("invokeId:"+sys.get("invokeId")+",projectCode："+projectCode+",enterpointer:"+enterPoint+",businessInput:"+JSON.toJSONString(busiInput));
		long start = System.currentTimeMillis();
		String  currDateTime = AppConfiguration.dateTimeMap.get(projectCode+"_"+enterPoint) ;
		try {
			if(AppConfiguration.RuleProjMetaMap.get(projectCode+"_"+currDateTime)==null 
					||AppConfiguration.AppRuleProjDataPools.get(projectCode+"_"+enterPoint)==null
					||AppConfiguration.ruleEnterMap.get(projectCode)==null
					||AppConfiguration.ruleEnterMap.get(projectCode).get(enterPoint)==null) {
				ruleDataCompileService.prepareDataToCacheRuleExe(null, projectCode, enterPoint);
			}
			//调用服务
			//默认走冠军
			JSONObject jsonObj= new JSONObject();
			jsonObj.put("champion", "true");
			busiInput.put("championInfo", jsonObj);
			output = ruleEngineService.invokeRule_citic(projectCode, enterPoint, busiInput, droolsRuleLog);
			sys.put("isSuccess", "1");
			sys.put("projectCode", projectCode);
		} catch (Exception e) {
			sys.put("exception", IOUtils.getStackTraceMsg(e));
			sys.put("exceptionType", "4");
			sys.put("isSuccess", "0");
		}finally {
			sys.put("ruleExecutResultList", droolsRuleLog.getLogs());
			sys.put("invokeTimes", System.currentTimeMillis() - start);
			output.put("System", sys);
		}
		logRuleExe(projectCode,enterPoint,busiInput,output,sys,droolsRuleLog,batchNo,null);
		return output;
	}
	
	public RuleDataEngineService getRuleEngineService() {
		return ruleEngineService;
	}
	public void setRuleEngineService(RuleDataEngineService ruleEngineService) {
		this.ruleEngineService = ruleEngineService;
	}
	public RuleResourceLoadService getRuleResourceLoadService() {
		return ruleResourceLoadService;
	}
	public void setRuleResourceLoadService(RuleResourceLoadService ruleResourceLoadService) {
		this.ruleResourceLoadService = ruleResourceLoadService;
	}
	@Override
	public String ruleFlowParamInfo(String projectCode, String enterPoint) {
		return this.ruleFlowParamExportService.obtainFlowParamInfo(projectCode, enterPoint);
	}
	@Override
	public String ruleDatacommponetParamInfo(String ruleProjectName, String enterPoint) {
		return this.ruleFlowParamExportService.obtainDatacommponetParamInfo(ruleProjectName, enterPoint);
	}
	public RuleFlowParamExportService getRuleFlowParamExportService() {
		return ruleFlowParamExportService;
	}
	public void setRuleFlowParamExportService(RuleFlowParamExportService ruleFlowParamExportService) {
		this.ruleFlowParamExportService = ruleFlowParamExportService;
	}
	@Override
	public String ruleProjectParamInfo() {
		return this.ruleFlowParamExportService.obtainFlowParamInfo();
	}
	
	/**
	 * 获取规则并加载缓存
	 * @param projectCode
	 * @param enterPointer
	 */
	public void compileRuleData(String projectCode, String enterPointer) throws Exception{
		AppConfiguration.funcMap.put(projectCode, null);
		ruleResourceLoadService.loadProjectDataMapping(projectCode);
		Map<String, EnterPoint> reMap = null;
		if (enterPointer != null) {
			String[] pointArg = StringUtils.split(enterPointer, ";");
			if (pointArg != null) {
				for (String str : pointArg) {
					try {						
						ruleResourceLoadService.loadEnterPoint(projectCode, str);
						reMap = AppConfiguration.ruleEnterMap.get(projectCode);
						reMap.get(str).setStatus("0");
						ruleResourceLoadService.loadEnterPointDeployResource(projectCode, str);
						ruleResourceLoadService.loadProjectRefeDrl(projectCode,str);
						reMap.get(str).setStatus("1");
						AppConfiguration.AppRuleProjChampionChallengerInfo.remove(projectCode+"_"+str);
						AppConfiguration.RuleFlowMetaMap.put(projectCode+"_"+str, null);
					}catch(Exception e) {
		   				//log.error("加载项目：" + projectCode + ",加载入口调用编码："+str+"异常");
		   				String error = ExceptionUtils.getStackTrace(e);
		   				loggger.error("rule_load_exception 异常加载项目【rule_sdk】" + projectCode + ",加载入口调用编码："+str+"异常;" + "message【"+error+"】");
		   				//log.error(error);
					}
				}
			}
		}
	   Map<String,KieBase> kieMap = AppCacheInfo.kieMap.get(projectCode);
	   if (kieMap != null) {
		   kieMap.clear();
	   }
	   ruleResourceLoadService.loadRuleFunctionDrl(projectCode);
       ruleResourceLoadService.loadProjectPmml(projectCode);
       ruleResourceLoadService.loadProjectPmmlRefData(projectCode);
	}
	
	/**
	 * 初始化默认值并补全参数信息
	 * @param projectCode
	 * @param enterPoint
	 * @return
	 */
	public Map<String, Object> initRuleParamMap(String projectCode,String enterPoint,Map<String, Object> businessMap) {
		Map<String, MClass>  mapClass =ruleFlowParamService.findDeployRuleFlowParam(projectCode, enterPoint);
		for (Entry<String, MClass> classEntry : mapClass.entrySet()) {
			if(!businessMap.containsKey(classEntry.getKey())) {
				businessMap.put(classEntry.getKey(), new HashMap<String,Object>());
			}
			//内置变量不初始化
			if("funParam".equals(classEntry.getKey()) || "builtinParam".equals(classEntry.getKey())) {
				continue;
			}
			Map<String, Object> fieldMap = (Map<String, Object>) businessMap.get(classEntry.getKey());
			for(MField mfield : classEntry.getValue().getParams()) {
				if((fieldMap.get(mfield.getName())==null 
						|| "".equals(fieldMap.get(mfield.getName()).toString().trim()))
						&& mfield.getDefaultValue()!=null && !StringUtils.isEmpty(mfield.getDefaultValue().trim())
						) {
					fieldMap.put(mfield.getName(), getObjectByType(mfield,true));
				}
			}
		}
		return businessMap;
	}
	private Object getObjectByType(MField mfield, boolean defalutValue) {
		String type = mfield.getType();
		if(defalutValue){
			if ("list".equalsIgnoreCase(mfield.getCollection())) {
				return new ArrayList<>();
			}
			if("String".equalsIgnoreCase(type)){
				return StringUtils.isBlank(mfield.getDefaultValue())?"":mfield.getDefaultValue();
			}else if("Long".equalsIgnoreCase(type)) {
				return StringUtils.isBlank(mfield.getDefaultValue())? 0L :Long.parseLong(mfield.getDefaultValue());
			}else if("Double".equalsIgnoreCase(type)) {
				return StringUtils.isBlank(mfield.getDefaultValue())? 0d :Double.parseDouble(mfield.getDefaultValue());
			}else if("Integer".equalsIgnoreCase(type)) {
                return StringUtils.isBlank(mfield.getDefaultValue())? 0 :Integer.parseInt(mfield.getDefaultValue());
            }else if("Float".equalsIgnoreCase(type)) {
                return StringUtils.isBlank(mfield.getDefaultValue())? 0f :Float.parseFloat(mfield.getDefaultValue());
            }else if("Date".equalsIgnoreCase(type)) {
			    return mfield.getDefaultValue();
			}else if("Boolean".equalsIgnoreCase(type)) {
				if(StringUtils.isNotBlank(mfield.getDefaultValue())){
					if("true".equalsIgnoreCase(mfield.getDefaultValue())){
						return Boolean.TRUE;
					}else{
						return Boolean.FALSE;
					}
				}
			} else {
				return null;
			}
			
		}
		if("String".equalsIgnoreCase(type)){
			return "StringValue";
		}else if("Long".equalsIgnoreCase(type)) {
			return 1L;
		}else if("Double".equalsIgnoreCase(type)) {
			return 1.1;
		}else if("Integer".equalsIgnoreCase(type)) {
            return 1;
        }else if("Float".equalsIgnoreCase(type)) {
            return 1.1f;
        }else if("Date".equalsIgnoreCase(type)) {
			return mfield.getDefaultValue();
		}else if("Boolean".equalsIgnoreCase(type)) {
			return Boolean.FALSE;
		} else {
			return null;
		}
	}
	/**
	 * 记录执行日志信息
	 * @param projectCode
	 * @param enterPoint
	 * @param businessInput
	 * @param result
	 * @param sys
	 * @param droolsRuleLog
	 */
	private void log(final String projectCode,
			final String enterPoint,
			final Map<String, Object> busiInput,
			final Map<String, Object> result,
			final Map<String, Object> sys,
			final DroolsRuleLog droolsRuleLog,
			final String batchNo,
			final String uuidStr) {
		if("1".equals(AppConfiguration.isNeedLog)) {
			final String oribusinessMapStr = JSON.toJSONString(busiInput);
//			final Map<String, Object> busiInput = JSON.parseObject(inputParam,Map.class);
			if(StringUtils.isEmpty(batchNo)) {//如果需要找挑战者的话，需要开启异步线程
				logExecutorService.execute(new Runnable() {
					public void run() {
						String newBatchNo = logtToService(projectCode,enterPoint,oribusinessMapStr,result,sys,droolsRuleLog,batchNo,uuidStr);
						//找到需要执行的挑战者分支信息，如果存在，则执行规则
						if(!AppConfiguration.AppRuleProjChampionChallengerInfo.containsKey(projectCode+"_"+enterPoint)) {
							bussinessSynchService.findChallengerInfo(projectCode,enterPoint,false);
						}
						String ccMapStr = AppConfiguration.AppRuleProjChampionChallengerInfo.get(projectCode+"_"+enterPoint);
						if(!RuleConstants.championchallengerempty.equals(ccMapStr)) {
							List<Map> ccMapList = JSON.parseArray(ccMapStr,Map.class);
							for (Map map : ccMapList) {
								if(!map.containsKey("champion") && map.get("challenger")!=null) {
									Map<String,Object> cmbusiInput = JSON.parseObject(oribusinessMapStr, Map.class);
									JSONObject newCondition = new JSONObject();
									newCondition.put("challenger", map.get("challenger").toString());
									cmbusiInput.put("championInfo", newCondition);
									cmbusiInput.remove("System");
									ruleEngineService(projectCode,enterPoint,"json",cmbusiInput,newBatchNo);
								}
							}
						}
					}
				});
			}else {
				logtToService(projectCode,enterPoint,oribusinessMapStr,result,sys,droolsRuleLog,batchNo,uuidStr);
			}
		}
	}
	
	/**
	 * 记录执行日志信息,针对rule_exe前缀的表
	 * @param projectCode
	 * @param enterPoint
	 * @param businessInput
	 * @param result
	 * @param sys
	 * @param droolsRuleLog
	 */
	private void logRuleExe(final String projectCode,
			final String enterPoint,
			final Map<String, Object> busiInput,
			final Map<String, Object> result,
			final Map<String, Object> sys,
			final DroolsRuleLog droolsRuleLog,
			final String batchNo,
			final String uuidStr) {
		if("1".equals(AppConfiguration.isNeedLog)) {
			final String oribusinessMapStr = JSON.toJSONString(busiInput);
			if(StringUtils.isEmpty(batchNo)) {//如果需要找挑战者的话，需要开启异步线程
				logExecutorService.execute(new Runnable() {
					public void run() {
						String newBatchNo = logtToService(projectCode,enterPoint,oribusinessMapStr,result,sys,droolsRuleLog,batchNo,uuidStr);
						//找到需要执行的挑战者分支信息，如果存在，则执行规则
						if(!AppConfiguration.AppRuleProjChampionChallengerInfo.containsKey(projectCode+"_"+enterPoint)) {
							bussinessSynchService.findChallengerInfo(projectCode,enterPoint,true);
						}
						String ccMapStr = AppConfiguration.AppRuleProjChampionChallengerInfo.get(projectCode+"_"+enterPoint);
						if(!RuleConstants.championchallengerempty.equals(ccMapStr)) {
							List<Map> ccMapList = JSON.parseArray(ccMapStr,Map.class);
							for (Map map : ccMapList) {
								if(!map.containsKey("champion") && map.get("challenger")!=null) {
									Map<String,Object> cmbusiInput = JSON.parseObject(oribusinessMapStr, Map.class);
									JSONObject newCondition = new JSONObject();
									newCondition.put("challenger", map.get("challenger").toString());
									cmbusiInput.put("championInfo", newCondition);
									cmbusiInput.remove("System");
									ruleEngineService_ruleExe(projectCode, enterPoint, cmbusiInput,newBatchNo);
								}
							}
						}
					}
				});
			}else {
				logtToService(projectCode,enterPoint,oribusinessMapStr,result,sys,droolsRuleLog,batchNo,uuidStr);
			}
		}
	}
	
	private String logtToService(String projectCode,
			String enterPoint,
			String busiInput,
			Map<String, Object> result,
			Map<String, Object> sys,
			DroolsRuleLog droolsRuleLog,
			String batchNo,
			String uuidStr) {
		if("1".equals(AppConfiguration.isNeedLog)) {
			DeployRuleLogs ruleLogs = new DeployRuleLogs(sys.get("invokeId").toString(),projectCode,enterPoint,busiInput);
			ruleLogs.setLogs(droolsRuleLog.getLogs());
			ruleLogs.setRuleflowid(droolsRuleLog.getRuleFlowId());
			ruleLogs.setVersion(droolsRuleLog.getVersion());
			if("1".equals(sys.get("isSuccess"))) {
				ruleLogs.setIssuccess("1");
			}else {
				ruleLogs.setIssuccess("0");
				ruleLogs.setExceptionType(sys.get("exceptionType").toString());
				ruleLogs.setExceptionMsg(sys.get("exception").toString());
			}
			String resultXml = JSON.toJSONString(result);
			ruleLogs.setRuleResult(resultXml);
			ruleLogs.setRunTime(sys.get("invokeTimes").toString());
			if(StringUtils.isEmpty(batchNo)) {
				batchNo = StringUtils.getUUID();
				ruleLogs.setLogType("1");
			}else {
				ruleLogs.setLogType("3");
			}
			ruleLogs.setBatchNo(batchNo);
			ruleLogService.saveRuleLog(ruleLogs);
			initKafkaResponse(projectCode,enterPoint,ruleLogs.getIssuccess(),ruleLogs.getLogType(),batchNo,result,uuidStr);
			return batchNo;
		}
		return null;
	}
	/**
	 * 初始化返回对象
	 * @param projectCode
	 * @param enterpointer
	 * @param isSuccess
	 * @param logtype
	 * @param batchNo
	 * @param busiInput
	 */
	private void initKafkaResponse(String projectCode,
			String enterpointer,
			String isSuccess,
			String logtype,
			String batchNo,
			Map<String, Object> busiInput,
			String uuidStr) {
	}
	@Override
	public String ruleExecutionLogList(String projectCode) {
//		String list = businessDBDao.ruleExecutionLogList(projectCode);
//	    return  list;
		return null;
	}
	@Override
	public String ruleExecutionLogDetails(String  projectCode,String ruleFlowId) {
		//String list = businessDBDao.RulegroupEcharsDetails(  projectCode, ruleFlowId);
	    return  null;
	}


	
}
